--author:lee
--date:2021.06.25
local require           = require
local ngx               = ngx
local debug             = debug
local string            = string
local type              = type
local table             = table
local pcall             = pcall
local xpcall            = xpcall
local math              = math
local math_floor        = math.floor
local tostring          = tostring
local string_find       = string.find
local table_tostring    = table.tostring

local log               = require("modules.commhm.log");
local ns_time           = require("modules.commhm.time");
local uu                = require("modules.commhm.utils");
local ns_ssdbDcClient   = require("modules.commhm.dc.ssdbDcClient");
local ns_const_vars     = require("modules.commhm.const_vars");
local ns_mysqlMgr       = require("modules.commhm.mysqlMgr");

local welfare_code      = require('modules.business_welfare.welfare_code')
local ns_config         = require("modules.business_welfare.welfare_config");
local biz_utils         = require("modules.business_common.business_utils")

local error_code         = welfare_code.error_code

local FOO =
{
    _VERSION				= '0.12.1',      -- 版本号
    worker_id               = ngx.worker.id(),
};


FOO.KEYS   =  ns_config.SSDB_KEYS


--初始化信息
FOO.init = function ()
    ngx.ctx.xff = biz_utils.real_client_ip() or "nil"
    if  (not FOO.ssdb_dc_client) and ns_config.ssdb_server_dc then
        FOO.ssdb_dc_client = ns_ssdbDcClient:new( "business_welfare",ns_config.ssdb_server_dc.server,
                                                                     ns_config.ssdb_server_dc.select )
        ngx.ctx.gg_final_has_called = false
    end

    --init mysql
	if  not FOO.cm_mysql_mgr and ns_config.mysql_server then
		log.debug( "create new cm_mysql_mgr" );
		FOO.cm_mysql_mgr = ns_mysqlMgr:new( "m_", ns_config.mysql_server,    ns_config.mysql_db_select  );
	end
end


FOO.final = function()
    if  ngx.ctx.gg_final_has_called then
        -- 防止无限循环 只调一次
    else
        ngx.ctx.gg_final_has_called = true;
        if  FOO.ssdb_dc_client then
            FOO.ssdb_dc_client:final(); --远程强制解锁
        end

        if  FOO.cm_mysql_mgr then
			FOO.cm_mysql_mgr:final();
		end
        ngx.ctx.__READ_ONLY__ = 0
    end
end


-- 异常退出
FOO.error_exit = function( txt )
    FOO.final()
    log.error(txt);
    ns_time.print_cost_all();
    ngx.exit(0);
end


FOO.return_table = function( t )
    t = t or {};

    if  type(t) ~= 'table' then
        t = { ret=t };
    end

    if  ngx.ctx.m_params.json == '1' then
        --json ajax 跨域
        ngx.header["Access-Control-Allow-Origin"] = "*";
        ngx.header["Server"] = "php";

        --log.debug("return json");
        local jret = uu.table2json( t );
        ngx.say(jret);

    elseif ngx.ctx.m_params.log == '1' then
        --log
        log.debug( "<br><br>=========================return begin========" );
        uu.var_dump(t)
        log.debug( "=========================return end========<br><br>" );

    else
        --lua
        local ret = table_tostring( t );
        ngx.say( ret);
    end
end


-----------------ssdb-------------------------------

-- get data from ssdb, unzip table
---no_cache  :  1=不使用缓存          其他(默认值)=使用缓存
---read_only :  1=只读，不加远程锁    其他(默认值)=加远程锁
FOO.private_hash_get_table_ssdb = function( uin_, key, hash, read_only,no_cache )
    local select_ = ns_config.select_ssdb(uin_) ---计算使用哪个SSDB
    if not no_cache then
        no_cache = 'y'  --默认=使用缓存
    end
    if not read_only then
        read_only = 0   --默认值 =加远程锁
    end

    local ret = FOO.ssdb_dc_client:read( select_, key, hash, FOO.error_exit,no_cache ,read_only);

    if  ret == ngx.null or ret == "" then
        log.debug( "get null value. select=" .. select_ .. ", key=" .. key .. ", hash=" .. hash );
        return error_code.ERROR_NO_DATA, {};
    else
        if  #ret > 1024*100 then
            log.day_list("business_welfare", "ssdb", "len", select_, key, hash, #ret );
        end

        local ret_table_ = table.loadstring( ret );
        if  type(ret_table_) == 'table' then

            if  not FOO.ssdb_dc_client:private_check_get_key( select_, key, hash, ret_table_ ) then
                log.day_list( "error_key_check", "get", select_, key, hash, ret, debug.traceback() );
                FOO.ssdb_dc_client:clean_shared_cache_ssdb( select_, key, hash );
                FOO.error_exit( "call hash_get_table_ssdb, [_k_] check fail." );
                return
            end

        else
            FOO.error_exit( "ssdb_error=" .. uu.to_str( ret ) .. ' ' .. key .. ' ' .. hash );
        end

        return error_code.COMMON_SUCCESS,  ret_table_ ;
    end

end


-- zip table, write data to ssdb
---delay       :  1=不立即写ssdb    其他(默认值)=立即同时写ssdb
---read_only   :  1=不解开远程锁    其他(默认值)=如果有远程锁，立即解锁
-----             （可作为主动参数传入=1，暂时不用解锁，等待final解锁，适用于一次读取，多次写入的情况）

FOO.private_hash_set_table_ssdb = function( uin_, key, hash, value,read_only,delay )
    local select_ = ns_config.select_ssdb(uin_) ---计算使用哪个SSDB
    --if not delay  then
        --delay = 0   --其他(默认值)=立即同时写ssdb
    --end
    if not read_only  then
        read_only = 0 --其他(默认值)=如果有远程锁，立即解锁
    end
    if  value and type(value) == 'table' then

        if  FOO.ssdb_dc_client:private_check_set_key( select_, key, hash, value ) then
            local v = table.tostring( value );
            FOO.ssdb_dc_client:save( select_, key, hash, v, FOO.error_exit,delay,read_only);
            return true
        else
            FOO.error_exit( "call hash_set_table_ssdb, [_k_] check fail." );
        end
    else
        FOO.error_exit( "call hash_set_table_ssdb, value is not table." );
    end
    return false
end


-- 多个请求
FOO.private_hash_mread_table_ssdb = function( uin_, kv_list )

    local select_    = ns_config.select_ssdb(uin_) ---计算使用哪个SSDB
    local ret_mread_ = FOO.ssdb_dc_client:mread( select_, kv_list, FOO.error_exit );

    local ret_list = {};

    for i=1, #ret_mread_ do
        local ret = ret_mread_[i];
        if  ret == ngx.null then
            table.insert( ret_list, { __key=kv_list[i] } );
        else
            local ret_table_ = table.loadstring( ret );
            if  type(ret_table_) == 'table' then
            else
                FOO.error_exit( "ssdb_error mread=" .. uu.to_str( ret ) );
            end

            ret_table_.__key = kv_list[i].key;
            table.insert( ret_list , ret_table_ );
        end
    end

    return ret_list;
end


--del key
FOO.private_hash_del_table_ssdb = function( uin_, key_, hash_ )

    local select_ = ns_config.select_ssdb(uin_) ---计算使用哪个SSDB
    return FOO.ssdb_dc_client:del( select_, key_, hash_, FOO.error_exit );
end

---------------------------------------------------------------
-- @Copyright：           迷你玩-wuwenkui
-- @Description：         首充福利需求 - 玩家首次充值信息 保存到ssdb

-- @function              set_ssdb_cache_info
-- @param ssdb_keyname    ssdb数据缓存 key名称
-- @param data_info       ssdb数据缓存 数据信息
-- @return                nil

-- @modify time           2021-05-14
---------------------------------------------------------------
FOO.set_ssdb_cache_info = function(ssdb_keyname ,data_info)
	if ssdb_keyname <= FOO.KEYS.NONE or ssdb_keyname >= FOO.KEYS.INVALID then
		log.error("error::set_ssdb_cache_info use ssdb_keyname error ,ssdb_keyname =" .. ssdb_keyname)
        return false
	end
	return FOO.private_hash_set_table_ssdb(ngx.ctx.m_params.uin, ngx.ctx.m_params.uin, ssdb_keyname, data_info)
end



---------------------------------------------------------------
-- @Copyright：           迷你玩-wuwenkui
-- @Description：         首充福利需求 - 从ssdb获取玩家首次充值信息

-- @function              get_ssdb_cache_info
-- @param ssdb_keyname    ssdb数据缓存 key名称

-- @return                table

-- @modify time           2021-05-14
---------------------------------------------------------------
FOO.get_ssdb_cache_info = function(ssdb_keyname, read_only)
	local ret, data = nil, nil

	if ssdb_keyname <= FOO.KEYS.NONE or ssdb_keyname >= FOO.KEYS.INVALID then
		log.error("error::get_ssdb_cache_info use ssdb_keyname error ,ssdb_keyname =" .. ssdb_keyname)
		return nil
	end

	--拉取变量
	ret, data = FOO.private_hash_get_table_ssdb(ngx.ctx.m_params.uin, ngx.ctx.m_params.uin, ssdb_keyname, read_only);
	if ret == 0 and data then
		return data
    elseif error_code.ERROR_NO_DATA == ret then
        return {}
    end

    log.error("error::get_ssdb_cache_info private_hash_get_table_ssdb failed ,ssdb_keyname =" .. ssdb_keyname)
	return nil;
end



--检查sql安全  --不能含有分号
function FOO.check_sql( sql_ )
	if  string_find( sql_, ';' ) then
		FOO.error_exit( "sql_error|" .. sql_ );
	end
end

------------- 数据库接口 --------------
function FOO.call_mysql( sql_, connect_key_, no_quit )
	--检查sql语句
	log.debug( connect_key_ .. ": " .. sql_ );
	FOO.check_sql( sql_ );

	local t_ = ngx.now();
	local ok, res = FOO.cm_mysql_mgr:query( sql_, connect_key_ );
	local cost_ = math_floor( (ngx.now() - t_)*1000 + 0.5 );
	if  cost_ >= 100 then
		log.day( "mysql",  cost_ .. "|" .. sql_ );
	elseif ngx.cache.debug_log == 1 or ngx.cache.debug_log == 2 then
		log.day( "mysql",  cost_ .. "|" .. sql_ );
	end

	if  ok then
		return res;
	else
		if  no_quit then
			log.error( "call_mysql_error no_quit:" .. sql_ )
			return {}
		else
			FOO.error_exit( "call_mysql_error:" .. sql_ );
		end
	end

end



FOO.random = function(n, m)
    math.randomseed( os.clock()*math.random(1000000,90000000) + math.random(1000000,90000000) )
    return math.random( n, m )
end


return FOO;
